package com.scsoft.dataacceptor.server;

import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import com.scsoft.dataacceptor.mongo.MongoCollectData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;

@Component
public class WaterServer {
	private static final Logger log = LoggerFactory.getLogger(WaterServer.class);
	private volatile EventLoopGroup bossGroup;
	private volatile EventLoopGroup workerGroup;
	private volatile ServerBootstrap bootstrap;
	private volatile boolean closed = false;
	@Value("${water.host}")
	private String localHost = "127.0.0.1";
	@Value("${water.port}")
	private int localPort = 60006;
	@Value("${water.run}")
	private boolean run = false;

	@Autowired
	private MongoCollectData mongoCollectData;

	@PostConstruct
	public void init() {
		if (!run)
			return;
		this.closed = false;
		this.bossGroup = new NioEventLoopGroup();
		this.workerGroup = new NioEventLoopGroup();
		this.bootstrap = new ServerBootstrap();
		this.bootstrap.group(this.bossGroup, this.workerGroup);
		this.bootstrap.channel(NioServerSocketChannel.class);
		this.bootstrap.option(ChannelOption.SO_BACKLOG, Integer.valueOf(1024));
		this.bootstrap.childOption(ChannelOption.TCP_NODELAY, Boolean.valueOf(true));
		this.bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
		this.bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

		this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
			private static final int IDEL_TIME_OUT = 1200;
			private static final int READ_IDEL_TIME_OUT = 1200;
			private static final int WRITE_IDEL_TIME_OUT = 1200;

			protected void initChannel(SocketChannel ch) throws Exception {
				ChannelPipeline pipeline = ch.pipeline();
				pipeline.addLast("idleStateHandler", new IdleStateHandler(READ_IDEL_TIME_OUT, WRITE_IDEL_TIME_OUT, IDEL_TIME_OUT));
				ByteBuf delimiter = Unpooled.copiedBuffer(new byte[] { (byte) 0xC3 });
				pipeline.addLast(new DelimiterBasedFrameDecoder(2048, false, delimiter));
				pipeline.addLast(new WaterServerHandler(mongoCollectData));
			}
		});
		doBind();
	}

	protected void doBind() {
		if (this.closed) {
			return;
		}
		this.bootstrap.bind(this.localPort).addListener(new ChannelFutureListener() {
			public void operationComplete(ChannelFuture f) throws Exception {
				if (f.isSuccess()) {
					WaterServer.log.info("Started Water Server Success: " + WaterServer.this.localPort);
				} else {
					WaterServer.log.error("Started Water Server Failed: " + WaterServer.this.localPort);
					f.channel().eventLoop().schedule(new Runnable() {
						public void run() {
							WaterServer.this.doBind();
						}
					}, 1L, TimeUnit.SECONDS);
				}
			}
		});
	}

	public void close() {
		this.closed = true;
		this.bossGroup.shutdownGracefully();
		this.workerGroup.shutdownGracefully();
		log.info("Stopped Netty Tcp Server: " + this.localPort);
	}

	public int getLocalPort() {
		return this.localPort;
	}

	public void setLocalPort(int localPort) {
		this.localPort = localPort;
	}

	public String getLocalHost() {
		return localHost;
	}

	public void setLocalHost(String localHost) {
		this.localHost = localHost;
	}

	public String getServerIpPort() {
		return this.localHost + ":" + this.localPort;
	}
}